package hypeerweb;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import communications.GlobalObjectId;

public class SaveNodeVisitor implements Visitor {
	
	HyPeerWebDatabase db;
	
	PreparedStatement saveNodeStatement;
	PreparedStatement saveNeighborStatement;
	PreparedStatement saveSurrogateStatement;
	PreparedStatement saveInvSurrogateStatement;
	PreparedStatement saveNodeProxyStatement;
	
	public SaveNodeVisitor(HyPeerWebDatabase db, Connection dbConnection)
	{
		try
		{
			this.db = db;
			
			saveNodeStatement = dbConnection.prepareStatement(insert(
					   HyPeerWebDatabase.NODES, 
					   HyPeerWebDatabase.NODE_ID, 
					   HyPeerWebDatabase.STATE, 
					   HyPeerWebDatabase.FOLD_ID, 
					   HyPeerWebDatabase.S_FOLD_ID,
					   HyPeerWebDatabase.I_S_FOLD_ID,
					   HyPeerWebDatabase.LOCAL_OBJECT_ID));
			   
			saveNeighborStatement = dbConnection.prepareStatement(insert(
					HyPeerWebDatabase.NEIGHBORS, 
					HyPeerWebDatabase.NODE_ID, 
					HyPeerWebDatabase.NEIGHBOR_ID ));
			
			saveSurrogateStatement = dbConnection.prepareStatement(insert(
					HyPeerWebDatabase.S_NEIGHBORS, 
					HyPeerWebDatabase.NODE_ID, 
					HyPeerWebDatabase.S_NEIGHBOR_ID));
			
			saveInvSurrogateStatement = dbConnection.prepareStatement(insert(
					HyPeerWebDatabase.I_NEIGHBORS, 
					HyPeerWebDatabase.NODE_ID, 
					HyPeerWebDatabase.I_NEIGHBOR_ID));

			saveNodeProxyStatement = dbConnection.prepareStatement(insert(
					HyPeerWebDatabase.NODE_PROXIES, 
					HyPeerWebDatabase.NODE_ID, 
					HyPeerWebDatabase.MACHINE_ADDRESS,
					HyPeerWebDatabase.PORT_NUMBER,
					HyPeerWebDatabase.LOCAL_OBJECT_ID));
		}
		catch (SQLException e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace();
		}
	}
	
	public void saveNeighbor(Node node, Parameters parameters) {
		if (!node.isLocalNode())
		{
			node.accept(this, parameters);
		}
	}

	@Override
	public void visit(Node node, Parameters parameters) {
		
		if (!db.isNodeSaved(node.getWebIdValue()))
		{
			try
			{
				saveNodeStatement.setInt(1, node.getWebIdValue());
				saveNodeStatement.setInt(2, node.getState().getID());
				saveNodeStatement.setInt(3, node.getFold().getWebIdValue());
				saveNodeStatement.setInt(4, node.getSurFold().getWebIdValue());
				saveNodeStatement.setInt(5, node.getInvFold().getWebIdValue());
				saveNodeStatement.setInt(6, node.getLocalObjectId().getId());
				saveNodeStatement.addBatch();
				saveNodeStatement.executeBatch();

				saveNeighbor(node.getFold(), parameters);
				saveNeighbor(node.getSurFold(), parameters);
				saveNeighbor(node.getInvFold(), parameters);
				
				for (Node neighbor : node.getNeighbors())
				{
					System.out.println("=== Neighbor ===");
					System.out.println(node.getWebIdValue() + ", " + neighbor.getWebIdValue());

					saveNeighborStatement.setInt(1, node.getWebIdValue());
					saveNeighborStatement.setInt(2, neighbor.getWebIdValue());
					saveNeighborStatement.addBatch();
					saveNeighborStatement.executeBatch();

					saveNeighbor(neighbor, parameters);
				}
				
				for (Node neighbor : node.getSurNeighbors())
				{
					saveSurrogateStatement.setInt(1, node.getWebIdValue());
					saveSurrogateStatement.setInt(2, neighbor.getWebIdValue());
					saveSurrogateStatement.addBatch();
					saveSurrogateStatement.executeBatch();

					saveNeighbor(neighbor, parameters);
				}
				
				for (Node neighbor : node.getInvNeighbors())
				{
					saveInvSurrogateStatement.setInt(1, node.getWebIdValue());
					saveInvSurrogateStatement.setInt(2, neighbor.getWebIdValue());
					saveInvSurrogateStatement.addBatch();
					saveInvSurrogateStatement.executeBatch();

					saveNeighbor(neighbor, parameters);
				}
			}
			catch (SQLException e)
			{
				System.err.println(e.getMessage());
				e.printStackTrace();
			}
		}
	}

	@Override
	public void visit(NodeProxy node, Parameters parameters) {
		
		if (!db.isProxyNodeSaved(node.getWebIdValue()))
		{
			try
			{
				GlobalObjectId globalId = node.getGlobalObjectId();
				
				saveNodeProxyStatement.setInt(1, node.getWebIdValue());
				saveNodeProxyStatement.setString(2, globalId.getMachineAddr());
				saveNodeProxyStatement.setInt(3, globalId.getPortNumber().getValue());
				saveNodeProxyStatement.setInt(4, globalId.getLocalObjectId().getId());
				saveNodeProxyStatement.addBatch();
				saveNodeProxyStatement.executeBatch();
			}
			catch (SQLException e)
			{
				System.err.println(e.getMessage());
				e.printStackTrace();
			}
		}
	}
	
	/**
	* Convenience method fro creating sql insert statements.
	*
	* insert(TABLE, VALUE1, VALUE2, VALUE3) turns into
	* "insert into TABLE (VALUE1, VALUE2, VALUE3) values (?,?,?)"
	*
	* "insert into [table] ([values...]) values (?...)"
	*
	* @param table - the table you want to insert into
	* @param values - the values you want the table to take(in order of field)
	* @return a string representation sql insert statement
	*/
	private String insert(String table, String...values)
	{
		String insertStatement = "insert into "+table+" (";
		String valueStatement = "values (";
		   
		for(int i = 0; i < values.length-1; i++)
		{
			insertStatement += values[i]+", ";
			valueStatement += "?, ";
		}
		   
		insertStatement += values[values.length -1]+") ";
		valueStatement += "?);";
		insertStatement += valueStatement;
			   
		return insertStatement;
	}
}
